package com.geovis.emergency.spd.biz.zwdd.config.schedule;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.time.Duration;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 定时任务配置
 * --注册为子组件周期，保证定时器的执行
 *
 * @author linrf
 * @version V1.0
 * @date 2020/5/21 10:53
 */
@Slf4j
@Data
@Configuration
@EnableScheduling
@ConfigurationProperties(prefix = "scheduling")
@ConditionalOnProperty(prefix = "scheduling", name = "enable", havingValue = "true")
public class ScheduleConfig implements SchedulingConfigurer, SmartLifecycle {

    /**
     * 定时器 线程池
     */
    private final ReentrantLock mainLock = new ReentrantLock();
    /**
     * 定时器 线程池
     */
    private final Condition termination = mainLock.newCondition();
    /**
     * 定时器 线程池
     */
    private final List<AbsScheduleHandler> absScheduleHandlers = new LinkedList<>();
    /**
     * 定时器 线程池
     */
    private final ScheduledThreadPoolExecutor executor;

    /**
     * 是否启动scheduling
     */
    private boolean enable = false;
    /**
     * 关闭超时时间
     * 默认:10s
     */
    private Duration shutdownTimeout = Duration.ofSeconds(10);
    /**
     * 关闭等待次数
     * 默认:3
     */
    private int shutdownTryTimes = 3;

    /**
     * 初始化
     */
    public ScheduleConfig() {
        // 核心线程数
        int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() >> 1, 1);
        ScheduledThreadPoolExecutor scheduledExcutor = new ScheduledThreadPoolExecutor(
                corePoolSize,
                r -> {
                    Thread t = new Thread(r, "schedule");
                    t.setDaemon(true);
                    return t;
                },
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
        // 设置最大线程数--两倍核心线程数
        scheduledExcutor.setMaximumPoolSize(corePoolSize << 1);
        // 保活30分钟
        scheduledExcutor.setKeepAliveTime(30, TimeUnit.MINUTES);
        // 容器关闭后，不执行队列中的延时任务,默认：立即执行
        scheduledExcutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
        // 容器关闭后，执行执行中的延时任务,默认：继续执行
        scheduledExcutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
        executor = scheduledExcutor;

        // 注册定时器配置
        AbsScheduleHandler.setScheduleConfig(this);
    }

    /**
     * 注册定时器类
     */
    @Autowired(required = false)
    public void init(List<AbsScheduleHandler> absScheduleHandlers) {
        if (null != absScheduleHandlers) {
            this.absScheduleHandlers.addAll(absScheduleHandlers);
        }
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(executor);
    }

    @Override
    public void start() {
        // 忽略执行
    }

    @Override
    public void stop() {
        // 关闭线程池
        executor.shutdown();
        log.info("All scheduler is stopping ...");
        // 定时器是否关闭
        long nanos = 0;
        int times = 0;
        for (AbsScheduleHandler scheduleHandler : this.absScheduleHandlers) {
            nanos = shutdownTimeout.toNanos();
            times = shutdownTryTimes;
            String shName = scheduleHandler.getClass().getSimpleName();
            mainLock.lock();
            try {
                while (true) {
                    if (scheduleHandler.isShutdown()) {
                        // 已经关闭
                        log.info("{}(Scheduler) is stoped", shName);
                        break;
                    }
                    if (nanos <= 0) {
                        if (times-- > 1) {
                            nanos = shutdownTimeout.toNanos();
                            log.warn("{}(Scheduler) is stop timeout. Retry times are {}", shName, times);
                            continue;
                        }
                        // 超时，未关闭
                        log.info("{}(Scheduler) is stop timeout. Ignore it", shName);
                        break;
                    }
                    nanos = termination.awaitNanos(nanos);
                }
            } catch (InterruptedException interruptedException) {
                log.info("{}(Scheduler) stop error：{}", shName, interruptedException.getMessage());
            } finally {
                mainLock.unlock();
            }
        }
        log.info("All scheduler is stoped");
    }

    @Override
    public boolean isRunning() {
        return enable && !executor.isShutdown();
    }
}
